package main

import (
	. "fmt"
	"reflect"
)

type Person struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Gender string `json:"gender"`
}

//func (p Person) GetSum(n1 int, n2 int) int {
func (p *Person) GetSum(n1 int, n2 int) int {
	return n1 + n2
}

//func (p Person) Set(name string, age int, gender string) {
func (p *Person) Set(name string, age int, gender string) {
	p.Name = name
	p.Age = age
	p.Gender = gender
}

//func (p Person) OutPut() {
func (p *Person) OutPut() {
	Println(p)
}

func StructRflect(r interface{}) {
	// 获取类型
	rType := reflect.TypeOf(r)
	// 获取Value
	rVal := reflect.ValueOf(r)
	Println(rVal, rType)
	// 获取r对应类别
	kind := rVal.Kind() //返回reflect.Type。整型和浮点型都为64位，即reflect.Int64 reflect.Float64
	//如果传入的不是struct。退出
	//if kind != reflect.Struct && kind != reflect.Ptr
	//或者
	if kind != reflect.Struct && rVal.Elem().Kind() != reflect.Struct {
		return
	}
	Println(kind)

	//获取结构体有几个字段
	//fieldTotal := rVal.NumField()

	//rVal.CanSet()表示是否可以重新设置其值，返回true可修改，否则不可
	Println("是否可改:", rVal.Elem().CanSet())
	rVal.Elem().FieldByName("Name").SetString("haha")
	rVal.MethodByName("OutPut").Call(nil)

	//如果传递的是指针
	fieldTotal := rVal.Elem().NumField()
	Printf("结构体有 %d 个字段\n", fieldTotal)
	for i := 0; i < fieldTotal; i++ {
		//遍历打印结构体字段名
		//fieldName := rType.Field(i)
		fieldName := rVal.Type().Elem().Field(i)
		Println(fieldName.Name)
	}

	// 获取结构体方法个数
	//methodTotal := rVal.NumMethod()

	//传递指针
	methodTotal := rVal.Elem().NumMethod()
	Printf("结构体有 %d 个方法\n", methodTotal)
	//for i:=0;i<methodTotal;i++{
	//	Println(i)
	//	funcName := rVal.Method(i)
	//	iFuncName := rVal.Interface()
	//	v := iFuncName.(Person)
	//	Println("funcName:", funcName)
	//	Println("iFuncName:", iFuncName)
	//	Println( v)
	//	Println("funcName:", runtime.FuncForPC(funcName.Pointer()).Name())
	//}

	method, ok := rType.MethodByName("OutPut")
	//method, ok := rType.Elem().MethodByName("OutPut")
	if ok {
		Println(method.Name)
	}

	Println("------------")
	// 方法并不是按顺序排列，而是按照ASCII码排序，（首字母顺序）
	rVal.Method(1).Call(nil) //nil表示不用传参
	//
	rVal.MethodByName("OutPut").Call(nil)
	//funcName := rType.Name()

	//传参则需要位[]reflect.Value的切片,相比于reflect.ValueOf.Set麻烦
	var data []reflect.Value
	data = append(data, reflect.ValueOf("花"))
	data = append(data, reflect.ValueOf(21))
	data = append(data, reflect.ValueOf("女"))
	Println(data)
	//执行Set方法
	rVal.MethodByName("Set").Call(data)
	//rVal.Elem().MethodByName("Set").Call(data)

	//如果使用此方式来设置结构体字段，结构体需以指针方式传参
	rVal.MethodByName("OutPut").Call(nil)
	//rVal.Elem().MethodByName("OutPut").Call(nil)

	//当有参数时，传递参数为[]reflect.Value{reflect.ValueOf(param),...}
	res := rVal.MethodByName("GetSum").Call([]reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)})
	Printf("%v --- %T\n", res, res)
	sum:=res[0].Interface().(int)
	Println(sum)


}

func main() {
	p := Person{
		Name:   "明",
		Age:    20,
		Gender: "男",
	}
	//StructRflect(p)
	StructRflect(&p)
}
